package com.supermap.client;

import com.supermap.crypto.CryproType;
import com.supermap.crypto.CryptoUtils;
import com.supermap.protocol.RpcDecoder;
import com.supermap.protocol.RpcEncoder;
import com.supermap.protocol.RpcRequest;
import com.supermap.protocol.RpcResponse;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.io.File;
import java.security.PublicKey;
import java.util.logging.Logger;

/**
 * @author liqs
 * @version 1.0
 * @date 2020/12/28 17:55
 */
public class NettyClient {

    private static Logger logger = Logger.getLogger(NettyClient.class.toString());
    private String host;
    private int port;
    private Channel channel = null;
    private EventLoopGroup group = new NioEventLoopGroup();
    Bootstrap b = null;

    public NettyClient(String host, int port, CryproType cryptoType) {
        NettyClientUtils.cryproType = cryptoType;
        loadServerPublic(host, port);
        this.host = host;
        this.port = port;
        // 生成客户端公私钥，内部可见，即每次都不一样
        NettyClientUtils.generateClientKeyPair(512);
    }

    public int getPort() {
        return port;
    }

    public String getHost(){
        return host;
    }

    public CryproType getCryproType(){
        return NettyClientUtils.getCryproType();
    }


    public void start() throws Exception {
        Bootstrap b = new Bootstrap();
        b.group(group).channel(NioSocketChannel.class)  // 使用NioSocketChannel来作为连接用的channel类
                .handler(new ChannelInitializer<SocketChannel>() { // 绑定连接初始化器
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
//                        logger.info("start connection server ...");
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new RpcEncoder(RpcRequest.class)); //编码request
                        pipeline.addLast(new RpcDecoder(RpcResponse.class)); //解码response
                        pipeline.addLast(new ClientHandler()); //客户端处理类

                    }
                });
        //发起异步连接请求，绑定连接端口和host信息
        final ChannelFuture future = b.connect(host, port).sync();
        future.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture arg0) throws Exception {
                if (future.isSuccess()) {
//                    logger.info("success connnection server , server info : " + host + ":" + port);
                } else {
                    logger.warning("filed connection server , please check the Server( ip : " + host + "port : + " + port + ") is exists");
                    future.cause().printStackTrace();
                    group.shutdownGracefully(); //关闭线程组
                }
            }
        });
        this.channel = future.channel();
    }

    public Channel getChannel() {
        return channel;
    }


    public void loadServerPublic(String serverHost, int serverPort) {

        if (NettyClientUtils.cryproType == CryproType.NOCRY || NettyClientUtils.cryproType.equals("")) {
            NettyClientUtils.serverPublicKey = null;
            logger.info("Current communication is not encrypted");
            return;
        }

        // 路径替换为 temp 路径
        String filepath = System.getProperty("java.io.tmpdir")
                + "/supermap_" + serverHost.replace(".", "_") + "_" + serverPort + ".pem";
        File file = new File(filepath);
        try {
            if (file.exists()) {
                if (NettyClientUtils.cryproType == CryproType.RSA) {
                    NettyClientUtils.serverPublicKey = CryptoUtils.getRSAPublicKeyFromFile(filepath);
                    //logger.info("Current communication is encrypted by RSA");
                }

                //logger.info("success load server publicKey , file path is " + filepath);
            } else {
                //启动client服务
                //NettyClient clientTemp = new NettyClient(serverHost, serverPort, cryptoType);
                this.host = serverHost;
                this.port = serverPort;
                // 生成客户端公私钥，内部可见，即每次都不一样
                NettyClientUtils.generateClientKeyPair(512);
                start();
                //Channel channel = getChannel();
                NettyClientUtils.getServerPublickey(channel, serverHost, serverPort);
                shutDownClient();
//                logger.info("success load server publicKey , file path is " + filepath);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 阻塞函数，最后调用，调用之后会向 server 发送关闭 client 服务的请求，然后 client 接收到后主动关闭连接
     */
    public void shutDownClient() {
        try {
            RpcRequest request = new RpcRequest();
            request.setInfoPurposes("closeClient");
            channel.writeAndFlush(request);
            while (channel.isActive()) {
            }
            channel.close();
            group.shutdownGracefully();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public byte[] encrypt(String msg) {
        return NettyClientUtils.encrypt(msg);
    }

    public PublicKey getClientPublicKey() {
        return NettyClientUtils.getClientPublicKey();
    }

    public ChannelFuture writeAndFlush(RpcRequest request) {
        if (channel == null) {
            logger.info("channel is null,please check if client is start");
        }

        if (channel.isActive()) {
            return channel.writeAndFlush(request);
        } else {
            //重连10次还是不行就直接返回null
            for (int i = 0; i < 10; i++) {
                try {
                    start();
                    if (channel.isActive()){
                        return channel.writeAndFlush(request);
                    }
                } catch (Exception e) {
                    logger.info(e.getMessage());
                }
            }

            return null;
        }
    }

}
